package com.smart.community.region.controller;

import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.smart.community.commons.annotation.ApiLog;
import com.smart.community.commons.enums.ApiLogOperationType;
import com.smart.community.commons.exception.BusinessException;
import com.smart.community.commons.result.Result;
import com.smart.community.commons.utils.SecurityUtils;
import com.smart.community.feign.property.service.DataScopeFeign;
import com.smart.community.feign.property.service.IPropertyCompanyCommunityFeign;
import com.smart.community.region.dto.ZoneInfoUpdateDTO;
import com.smart.community.region.entity.Zone;
import com.smart.community.region.service.IZoneService;
import com.smart.community.region.vo.ZoneDetailVO;
import com.smart.community.region.vo.ZoneListVO;
import com.smart.community.region.vo.ZoneWithBuildingsVO;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;

/**
 * 分区管理控制器
 * 提供分区管理相关的REST API接口
 * 
 * 严格按照《Java后端SpringBoot代码开发规范_重构版.md》Controller层规范实现：
 * - 路径规范：以模块名开头，不使用/api前缀
 * - 异常处理：异常往外抛，添加throws Exception声明
 * - 日志记录：统一的日志格式，提升可读性和可维护性
 * - 参数验证：完善的参数验证和错误提示
 * - 接口调用：调用Service接口，不直接调用实现类
 * - 职责单一：Controller只负责请求处理和响应封装，不包含复杂业务逻辑
 * 
 * @author Wu.Liang
 * @since 2025-01-14
 * @version 1.0.0
 */
@Slf4j
@RestController
@RequestMapping({"/region/zone", "/region/zones"})
@Tag(name = "分区管理", description = "分区管理相关接口")
public class ZoneController {
	@Autowired
	private DataScopeFeign dataScopeFeign;
	@Autowired
    private IPropertyCompanyCommunityFeign propertyCompanyCommunityFeign;
    @Autowired
    private IZoneService zoneService;

    /**
     * 获取当前用户工作区域列表
     * 根据当前登录用户所属物业公司获取社区分区列表，格式为'社区名 - 分区名'
     * 
     * @return 工作区域列表
     * @throws Exception 统一异常
     */
    @GetMapping("/work-areas")
    @Operation(summary = "获取当前用户工作区域列表", description = "获取当前用户所属物业公司下的社区分区列表，格式为'社区名 - 分区名'")
    public Result<List<ZoneListVO>> getWorkAreas() throws Exception {
        log.info("开始获取当前用户工作区域列表");
        
        // 权限验证
        Long currentUserId = SecurityUtils.getCurrentUserId();
        if (currentUserId == null) {
            log.warn("用户未登录，无法获取工作区域列表");
            return Result.fail("用户未登录，请先登录");
        }
        
        List<Long> userPropertyCompanyIds = dataScopeFeign.getUserPropertyCompanyIds(currentUserId);
        if (userPropertyCompanyIds == null || userPropertyCompanyIds.size()==0) {
            log.warn("用户{}未关联物业公司，无法获取工作区域列表", currentUserId);
            return Result.fail("用户未关联物业公司，无法获取工作区域列表");
        }
        
        // 调用Service层获取工作区域列表
        List<ZoneListVO> result = zoneService.getZoneListByPropertyCompanyId(userPropertyCompanyIds);
        
        log.info("用户{}工作区域列表获取完成，结果数量：{}", currentUserId, result != null ? result.size() : 0);
        return Result.success(result);
    }

    /**
     * 根据社区ID获取分区列表（带楼栋信息）
     * 
     * @param communityId 社区ID
     * @return 分区列表
     * @throws Exception 统一异常
     */
    @GetMapping("/community/{communityId}")
    @Operation(summary = "根据社区ID获取分区列表", description = "获取指定社区下的所有分区信息，包含楼栋关联数据")
    public Result<List<ZoneWithBuildingsVO>> getZonesByCommunityId(
            @PathVariable Long communityId) throws Exception {
        log.info("根据社区ID获取分区列表，社区ID：{}", communityId);
        
        // 参数验证
        if (communityId == null) {
            log.warn("社区ID不能为空");
            return Result.fail("社区ID不能为空");
        }
        
        // 权限验证
        Long currentUserId = SecurityUtils.getCurrentUserId();
        if (currentUserId == null) {
            log.warn("用户未登录，无法获取分区列表");
            return Result.fail("用户未登录，请先登录");
        }
        
        // 调用Service层获取分区列表
        List<ZoneWithBuildingsVO> result = zoneService.getByCommunityId(communityId);
        
        log.info("社区{}分区列表获取完成，结果数量：{}", communityId, result != null ? result.size() : 0);
        return Result.success(result);
    }
    /**
     * 根据ID查询分区详情
     * 
     * @param id 分区ID
     * @return 分区详情
     */
    @GetMapping("/{id}")
    @Operation(summary = "查询分区详情", description = "根据分区ID查询分区详细信息")
    public Result<ZoneDetailVO> getZoneDetail(@Parameter(description = "分区ID") @PathVariable Long id) throws Exception {
        try {
            log.info("开始查询分区详情，操作人：{}，分区ID：{}", SecurityUtils.getCurrentUserId(), id);
            
            // 参数校验
            if (id == null || id <= 0) {
                return Result.fail("分区ID必须大于0");
            }
            
            // 权限控制
            Long currentUserId = SecurityUtils.getCurrentUserId();
            if (currentUserId == null) {
                return Result.fail("用户未登录");
            }
            
            ZoneDetailVO zoneDetail = zoneService.getZoneDetail(id);
            if (zoneDetail == null) {
                return Result.fail("分区不存在");
            }
            
            log.info("查询分区详情成功，分区ID：{}，操作人：{}", id, currentUserId);
            return Result.success("查询成功", zoneDetail);
        } catch (Exception e) {
            log.error("查询分区详情失败，操作人：{}，分区ID：{}，错误信息：{}", SecurityUtils.getCurrentUserId(), id, e.getMessage(), e);
            return Result.fail("查询失败：" + e.getMessage());
        }
    }

    /**
     * 更新分区基本信息
     * 
     * @param id 分区ID
     * @param dto 更新数据
     * @return 更新结果
     */
    @PutMapping("/{id}/info")
    @Operation(summary = "更新分区基本信息", description = "更新分区的基本信息，如名称、编码、类型等")
    @ApiLog(logTitle = "更新分区基本信息", logType = 2, moduleName = "区域管理", operationType = ApiLogOperationType.UPDATE)
    public Result<String> updateZoneInfo(
            @Parameter(description = "分区ID") @PathVariable Long id,
            @Parameter(description = "更新数据") @Valid @RequestBody ZoneInfoUpdateDTO dto) throws Exception {
        try {
            log.info("开始更新分区基本信息，操作人：{}，分区ID：{}，分区名称：{}", SecurityUtils.getCurrentUserId(), id, dto.getZoneName());
            
            // 参数校验
            if (id == null || id <= 0) {
                return Result.fail("分区ID必须大于0");
            }
            
            // 权限控制
            Long currentUserId = SecurityUtils.getCurrentUserId();
            if (currentUserId == null) {
                return Result.fail("用户未登录");
            }
            
            // 权限控制：只有管理员可以更新分区信息
            if (!SecurityUtils.hasRole("admin")) {
                return Result.fail("无权限更新分区信息");
            }
            
            // 获取当前分区信息以获取社区ID
            Zone existingZone = zoneService.getById(id);
            if (existingZone == null) {
                return Result.fail("分区不存在");
            }
            
            // 检查分区编码是否存在（在指定社区内，排除当前分区）
            if (zoneService.isZoneCodeExists(dto.getZoneCode(), existingZone.getCommunityId(), id)) {
                return Result.fail("该社区内分区编码已存在");
            }
            
            boolean success = zoneService.updateZoneInfo(id, dto, currentUserId);
            if (success) {
                log.info("更新分区基本信息成功，分区ID：{}，操作人：{}", id, currentUserId);
                return Result.success("更新成功");
            } else {
                log.error("更新分区基本信息失败，分区ID：{}，操作人：{}", id, currentUserId);
                return Result.fail("更新失败");
            }
        } catch (Exception e) {
            log.error("更新分区基本信息失败，操作人：{}，分区ID：{}，错误信息：{}", SecurityUtils.getCurrentUserId(), id, e.getMessage(), e);
            return Result.fail("更新失败：" + e.getMessage());
        }
    }

    /**
     * 创建新分区
     * 
     * @param zone 分区信息
     * @return 创建结果
     */
    @PostMapping
    @Operation(summary = "创建分区", description = "创建新的分区")
    @ApiLog(logTitle = "创建分区", logType = 2, moduleName = "区域管理", operationType = ApiLogOperationType.ADD)
    public Result<Zone> createZone(@Parameter(description = "分区信息") @Valid @RequestBody Zone zone) throws Exception {
        try {
            // 自动生成分区编码（在日志记录之前）
            String zoneCode = generateUniqueZoneCode();
            zone.setZoneCode(zoneCode);
            log.info("自动生成分区编码：{}", zoneCode);
            
            log.info("开始创建分区，操作人：{}，分区名称：{}，分区编码：{}，社区ID：{}", SecurityUtils.getCurrentUserId(), zone.getZoneName(), zone.getZoneCode(), zone.getCommunityId());
            
            // 参数校验
            if (zone.getCommunityId() == null) {
                return Result.fail("社区ID不能为空");
            }
            
            // 权限控制
            Long currentUserId = SecurityUtils.getCurrentUserId();
            if (currentUserId == null) {
                return Result.fail("用户未登录");
            }
            
            // 数据权限验证
            validateCommunityPermission(zone.getCommunityId());
            
            Long propertyCompanyId = propertyCompanyCommunityFeign.getPropertyCompanyIdByCommunityId(zone.getCommunityId());
            if (propertyCompanyId == null) {
                return Result.fail("该社区未关联物业公司，无法创建分区");
            }
            
            // 设置物业公司ID
            zone.setPropertyCompanyId(propertyCompanyId);
            zone.setCreateBy(currentUserId);
            zone.setUpdateBy(currentUserId);
            
            boolean success = zoneService.save(zone);
            if (success) {
                log.info("创建分区成功，分区ID：{}，操作人：{}", zone.getId(), currentUserId);
                return Result.success("创建成功", zone);
            } else {
                log.error("创建分区失败，分区名称：{}，操作人：{}", zone.getZoneName(), currentUserId);
                return Result.fail("创建失败");
            }
        } catch (Exception e) {
            log.error("创建分区失败，操作人：{}，分区名称：{}，错误信息：{}", SecurityUtils.getCurrentUserId(), zone.getZoneName(), e.getMessage(), e);
            return Result.fail("创建失败：" + e.getMessage());
        }
    }

    /**
     * 更新分区信息
     * 
     * @param id 分区ID
     * @param zone 分区信息
     * @return 更新结果
     */
    @PutMapping("/{id}")
    @Operation(summary = "更新分区", description = "更新分区信息")
    @ApiLog(logTitle = "更新分区", logType = 2, moduleName = "区域管理", operationType = ApiLogOperationType.UPDATE)
    public Result<String> updateZone(
            @Parameter(description = "分区ID") @PathVariable Long id,
            @Parameter(description = "分区信息") @Valid @RequestBody Zone zone) throws Exception {
        try {
            log.info("开始更新分区，操作人：{}，分区ID：{}，分区名称：{}", SecurityUtils.getCurrentUserId(), id, zone.getZoneName());
            
            // 参数校验
            if (id == null || id <= 0) {
                return Result.fail("分区ID必须大于0");
            }
            
            // 权限控制
            Long currentUserId = SecurityUtils.getCurrentUserId();
            if (currentUserId == null) {
                return Result.fail("用户未登录");
            }
            
            // 权限控制：只有管理员可以更新分区
            if (!SecurityUtils.hasRole("admin")) {
                return Result.fail("无权限更新分区");
            }
            
            // 数据权限验证
            validateZonePermission(id);
            
            zone.setId(id);
            
            // 检查分区编码是否存在（在指定社区内，排除当前分区）
            if (zoneService.isZoneCodeExists(zone.getZoneCode(), zone.getCommunityId(), id)) {
                return Result.fail("该社区内分区编码已存在");
            }
            
            zone.setUpdateBy(currentUserId);
            
            boolean success = zoneService.updateById(zone);
            if (success) {
                log.info("更新分区成功，分区ID：{}，操作人：{}", id, currentUserId);
                return Result.success("更新成功");
            } else {
                log.error("更新分区失败，分区ID：{}，操作人：{}", id, currentUserId);
                return Result.fail("更新失败");
            }
        } catch (Exception e) {
            log.error("更新分区失败，操作人：{}，分区ID：{}，错误信息：{}", SecurityUtils.getCurrentUserId(), id, e.getMessage(), e);
            return Result.fail("更新失败：" + e.getMessage());
        }
    }

    /**
     * 删除分区
     * 
     * @param id 分区ID
     * @return 删除结果
     */
    @DeleteMapping("/{id}")
    @Operation(summary = "删除分区", description = "删除指定分区")
    @ApiLog(logTitle = "删除分区", logType = 2, moduleName = "区域管理", operationType = ApiLogOperationType.DELETE)
    public Result<String> deleteZone(@Parameter(description = "分区ID") @PathVariable Long id) throws Exception {
        try {
            log.info("开始删除分区，操作人：{}，分区ID：{}", SecurityUtils.getCurrentUserId(), id);
            
            // 参数校验
            if (id == null || id <= 0) {
                return Result.fail("分区ID必须大于0");
            }
            
            // 权限控制
            Long currentUserId = SecurityUtils.getCurrentUserId();
            if (currentUserId == null) {
                return Result.fail("用户未登录");
            }
            
            // 权限控制：只有管理员可以删除分区
            if (!SecurityUtils.hasRole("admin")) {
                return Result.fail("无权限删除分区");
            }
            
            // 数据权限验证
            validateZonePermission(id);
            
            boolean success = zoneService.removeById(id);
            if (success) {
                log.info("删除分区成功，分区ID：{}，操作人：{}", id, currentUserId);
                return Result.success("删除成功");
            } else {
                log.error("删除分区失败，分区ID：{}，操作人：{}", id, currentUserId);
                return Result.fail("删除失败");
            }
        } catch (Exception e) {
            log.error("删除分区失败，操作人：{}，分区ID：{}，错误信息：{}", SecurityUtils.getCurrentUserId(), id, e.getMessage(), e);
            return Result.fail("删除失败：" + e.getMessage());
        }
    }

    // ==================== 业务查询接口 ====================


    /**
     * 查询物业公司分区列表
     * 
     * @param propertyCompanyId 物业公司ID
     * @return 分区列表
     */
    @GetMapping("/property-company/{propertyCompanyId}")
    @Operation(summary = "查询物业公司分区列表", description = "根据物业公司ID查询分区列表")
    public Result<List<Zone>> getZonesByPropertyCompany(@Parameter(description = "物业公司ID") @PathVariable Long propertyCompanyId) throws Exception {
        try {
            log.info("开始查询物业公司分区列表，操作人：{}，物业公司ID：{}", SecurityUtils.getCurrentUserId(), propertyCompanyId);
            
            // 参数校验
            if (propertyCompanyId == null || propertyCompanyId <= 0) {
                return Result.fail("物业公司ID必须大于0");
            }
            
            // 权限控制
            Long currentUserId = SecurityUtils.getCurrentUserId();
            if (currentUserId == null) {
                return Result.fail("用户未登录");
            }
            
            List<Zone> zones = zoneService.getByPropertyCompanyId(propertyCompanyId);
            
            log.info("查询物业公司分区列表成功，物业公司ID：{}，分区数量：{}，操作人：{}", propertyCompanyId, zones.size(), currentUserId);
            return Result.success("查询成功", zones);
        } catch (Exception e) {
            log.error("查询物业公司分区列表失败，操作人：{}，物业公司ID：{}，错误信息：{}", SecurityUtils.getCurrentUserId(), propertyCompanyId, e.getMessage(), e);
            return Result.fail("查询失败：" + e.getMessage());
        }
    }

    /**
     * 检查分区编码是否存在（在指定社区内）
     * 
     * @param zoneCode 分区编码
     * @param communityId 社区ID
     * @param excludeId 排除的分区ID
     * @return 是否存在
     */
    @GetMapping("/check-code")
    @Operation(summary = "检查分区编码", description = "检查分区编码是否已在指定社区内存在")
    public Result<Boolean> checkZoneCode(
            @Parameter(description = "分区编码") @RequestParam String zoneCode,
            @Parameter(description = "社区ID") @RequestParam Long communityId,
            @Parameter(description = "排除的分区ID") @RequestParam(required = false) Long excludeId) throws Exception {
        try {
            log.info("开始检查分区编码，操作人：{}，编码：{}，社区ID：{}，排除ID：{}", SecurityUtils.getCurrentUserId(), zoneCode, communityId, excludeId);
            
            // 参数校验
            if (zoneCode == null || zoneCode.trim().isEmpty()) {
                return Result.fail("分区编码不能为空");
            }
            
            if (communityId == null || communityId <= 0) {
                return Result.fail("社区ID必须大于0");
            }
            
            // 权限控制
            Long currentUserId = SecurityUtils.getCurrentUserId();
            if (currentUserId == null) {
                return Result.fail("用户未登录");
            }
            
            boolean exists = zoneService.isZoneCodeExists(zoneCode, communityId, excludeId);
            
            log.info("检查分区编码完成，编码：{}，社区ID：{}，存在：{}，操作人：{}", zoneCode, communityId, exists, currentUserId);
            return Result.success("查询成功", exists);
        } catch (Exception e) {
            log.error("检查分区编码失败，操作人：{}，编码：{}，社区ID：{}，错误信息：{}", SecurityUtils.getCurrentUserId(), zoneCode, communityId, e.getMessage(), e);
            return Result.fail("查询失败：" + e.getMessage());
        }
    }

    /**
     * 验证社区权限
     * 
     * @param communityId 社区ID
     */
    private void validateCommunityPermission(Long communityId) throws Exception {
        Long currentUserId = SecurityUtils.getCurrentUserId();
        log.debug("验证用户权限，用户ID: {}, 社区ID: {}", currentUserId, communityId);
        
        try {
            // 1. 检查超级管理员权限
            Boolean isSuperAdmin = dataScopeFeign.isUserSuperAdmin(currentUserId);
            if (Boolean.TRUE.equals(isSuperAdmin)) {
                log.debug("用户{}为超级管理员，拥有所有权限", currentUserId);
                return;
            }
            
            // 2. 验证社区权限
            Boolean hasCommunityPermission = dataScopeFeign.hasCommunityPermission(currentUserId, communityId);
            if (!Boolean.TRUE.equals(hasCommunityPermission)) {
                log.warn("用户{}无权限访问社区{}", currentUserId, communityId);
                throw new BusinessException("无权限访问该社区");
            }
            
            log.debug("用户{}权限验证通过，社区ID: {}", currentUserId, communityId);
        } catch (BusinessException e) {
            // 业务异常直接抛出
            throw e;
        } catch (Exception e) {
            log.error("权限验证失败，用户ID: {}, 社区ID: {}, 错误: {}", currentUserId, communityId, e.getMessage());
            // 权限验证失败时，默认拒绝访问，确保安全性
            throw new BusinessException("权限验证失败，请稍后重试");
        }
    }

    /**
     * 验证分区操作权限
     * 
     * @param zoneId 分区ID
     */
    private void validateZonePermission(Long zoneId) throws Exception {
        Long currentUserId = SecurityUtils.getCurrentUserId();
        
        try {
            // 1. 检查超级管理员权限
            Boolean isSuperAdmin = dataScopeFeign.isUserSuperAdmin(currentUserId);
            if (Boolean.TRUE.equals(isSuperAdmin)) {
                log.debug("用户{}为超级管理员，拥有所有权限", currentUserId);
                return;
            }
            
            // 2. 获取分区信息
            Zone zone = zoneService.getById(zoneId);
            if (zone == null) {
                throw new BusinessException("分区不存在");
            }
            
            // 3. 验证社区权限
            Boolean hasCommunityPermission = dataScopeFeign.hasCommunityPermission(currentUserId, zone.getCommunityId());
            if (!Boolean.TRUE.equals(hasCommunityPermission)) {
                log.warn("用户{}无权限操作社区{}的分区", currentUserId, zone.getCommunityId());
                throw new BusinessException("无权限操作该分区");
            }
            
            log.debug("用户{}分区权限验证通过，分区ID: {}", currentUserId, zoneId);
        } catch (BusinessException e) {
            // 业务异常直接抛出
            throw e;
        } catch (Exception e) {
            log.error("分区权限验证失败，用户ID: {}, 分区ID: {}, 错误: {}", currentUserId, zoneId, e.getMessage());
            // 权限验证失败时，默认拒绝访问，确保安全性
            throw new BusinessException("权限验证失败，请稍后重试");
        }
    }

    /**
     * 生成唯一的分区编码
     *
     * 使用StringUtils.generateRandomString32()方法生成32位随机字符串
     * 确保生成的编码在系统中唯一
     *
     * @return 唯一的分区编码
     */
    private String generateUniqueZoneCode() {
        String zoneCode;
        int maxAttempts = 10; // 最大尝试次数
        int attempts = 0;

        do {
            zoneCode = com.smart.community.commons.utils.StringUtils.generateRandomString32();
            attempts++;

            // 检查编码是否已存在（全局检查，不限制社区）
            if (!zoneService.isZoneCodeExists(zoneCode, null, null)) {
                log.info("生成唯一分区编码成功：{}，尝试次数：{}", zoneCode, attempts);
                return zoneCode;
            }

            log.warn("生成的分区编码已存在，重新生成：{}，尝试次数：{}", zoneCode, attempts);
        } while (attempts < maxAttempts);

        // 如果达到最大尝试次数仍未生成唯一编码，抛出异常
        log.error("生成唯一分区编码失败，已达到最大尝试次数：{}", maxAttempts);
        throw new BusinessException("生成唯一分区编码失败，请稍后重试");
    }
}